package relu.fbs.xt;


import java.io.FileWriter;
import java.text.DecimalFormat;
import java.util.*;
import java.io.*;


import relu.fbs.BadDataException;
import relu.fbs.FarmData;

import jfm.model.Types.CropType;


public class FBSFarmData extends FarmData {
	public final int farmId;
	private boolean isBad=false;
	public boolean isBad(){return isBad;}
	public void flagBadFarm(){
		isBad=true;
	}
	
	// Once upon a time this function used to return null pointers if no crop existed. 
	public FBSCropData getCrop(CropType ct){
		if ( crops.containsKey(ct)){
			return crops.get(ct);
		} else {
			
			throw new Error("Attempt to get non-existent crop "+ct+" for farm "+farmId);
		}
	}

	public enum FBSFarmDatum {
		FARMID ("farmid"),
		REGION ("region"),
		COUNTY ("county"),
		ALTITUDE ("altitude"),
		
		// About the farm business
		FORMOFBUSINESS ("formOfBusiness"), // row 5
		INCONSERVATIONSCHEME ("inConservationScheme"), // row 33
		NETFARMINCOME ("netfarmincome"),
		FARMERDOB ("farmerDOB"),
		FARMEREC ("farmerEducationCode"),
		FARMERTIMEWORKED ("farmerTimeWorked"),
		
		
		AREAELIGIBLEAAPS ("areaEligibleAAPS"),
		AREAELIGIBLESPS ("areaEligibleSPS"), // Post 2005
		TOTALMAINSETASIDE ("totalMainSetAside"),

		NRSETASIDEAREA ("areaNRSetAside"), // Non Rotational set aside .. pre 1994 
		NRSETASIDEEO ("eoNRSetAside"),
		NRSETASIDEPAYMENT ("subsidyNRSetAside"),
		
		SYSETASIDEAREA ("areaSYSetAside"), // Compulsory set aside .. pre 1994 
		SYSETASIDEEO ("eoSYSetAside"),
		SYSETASIDEPAYMENT ("subsidySYSetAside"),
		
		SYRSETASIDEAREA ("areaSYRSetAside"), // Compulsory set aside .. pre 1998 
		SYRSETASIDEEO ("eoSYRSetAside"),
		SYRSETASIDEPAYMENT ("subsidySYRSetAside"),
		CSETASIDEAREA ("areaCSetAside"), // Compulsory set aside .. pre 1998 
		CSETASIDEEO ("eoCSetAside"),
		CSETASIDEPAYMENT ("subsidyCSetAside"),
		SETASIDEAREA ("areaSetAside"),
		SETASIDEEO ("eoSetAside"),
		SETASIDEPAYMENT ("subsidySetAside"),
		
		UCAREA ("areaUC"), // Uncropped xcl fallow and set aside from 2005/6
		UCEO ("eoUC"),
		UCPAYMENT ("subsidyUC"),
		
		AREAELIGIBLESETASIDE ("areaEligibleSetAside"),
		TOTALGRASS ("totalGrass"),
		TOTALLABOURHOURS("totalLabourHours"),
		TOTALLABOURCOST("totalLabourCost"),
		TOTALMAINAREA("totalmainarea"),
		TOTALMAINCV("totalmaincv"),
		TOTALMAINREV("totalmainrev"),
		TOTALMAINFARMCONS("totalmainfarmconc"),
		TOTALMAINFARMFEED("totalmainfarmfeed"),
		TOTALMAINAREAPAY("totalmainareapay"),
		TOTALMAINAREAPAYMENTDUE("totalmainareapaymentdue"),
		TOTALMAINEO("totalmaineo"),
		
		TOTALFORAGEAREA("totalforagearea"),
		TOTALBYPRODUCTSAREA("totalbyproductsarea"),
		TOTALBYPRODUCTSOPENV("totalbyproductsopenv"),
		TOTALBYPRODUCTSCLOSEV("totalbyproductsclosev"),
		TOTALBYPRODUCTSREVXSUBS("totalbyproductsrevxsubs"),
		TOTALBYPRODUCTSFARMCONS("totalbyproductsfarmcons"),
		TOTALBYPRODUCTSSUBS("totalbyproductssubs"),
		TOTALBYPRODUCTSEO("totalbyproductseo"),
		UTILIZEDAREA("utilizedArea");

		public final String RName;
		private FBSFarmDatum(String rName_){
			RName=rName_;
		}
	}
	

	public void putFarmData(FBSFarmDatum nm,Double val) throws BadDataException {
		if ( farmData.containsKey(nm)){
			throw new BadDataException("Duplicate value of "+nm+" with val "+val+" and original "+farmData.get(nm)+" for farm "+farmId);
		}
		farmData.put(nm, val);
	}
	
	public void putCheckedFarmData(FBSFarmDatum nm,Double val) throws BadDataException {
		if ( farmData.containsKey(nm)){
			double diff = val-farmData.get(nm);
			if ( Math.abs(diff)> 1.0){	
				System.out.println("Duplicate value of "+nm+" with val "+val+" not same as original "+farmData.get(nm)+" for farm "+farmId);
//				throw new Error("Duplicate value of "+nm+" with val "+val+" not same as original "+farmData.get(nm));
			}
		}
		farmData.put(nm, val);
	}
	
	private final Map<FBSFarmDatum,Double> farmData = new LinkedHashMap<FBSFarmDatum,Double>();
	private final FBSPost2000DataReader p2000reader;
	
	private final Map<CropType,FBSCropData> crops = new LinkedHashMap<CropType,FBSCropData>();
	
	public Double getFarmDatum(FBSFarmDatum fd){
		return farmData.get(fd);
	}
	public boolean hasDatum(FBSFarmDatum dt){
		return farmData.containsKey(dt);
	}
	
	protected void init(){
		for ( CropType ct:fbsCropsKeySet){
			crops.put(ct, new FBSCropData(ct));
		}

	}
	
	public boolean hasBadCrops(){
		for(FBSCropData cdata:crops.values()){
			if ( cdata.isBad()){
				return true;
			}
		}
		return false;
	}
	
	public FBSFarmData(int fid){
		farmId=fid;
		init();
		p2000reader=new FBSPost2000DataReader(farmId,crops,this);
	}
	
	/** Create a farm object from csv data and calculate summary data.  
	 * Relies on the csv format having been written by the writeAllToCSV function of this class  */
	public FBSFarmData(String line){
		init();
		String[] bits=line.split(",");
		int i=0;
		for ( FBSFarmDatum nm:FBSFarmDatum.values()){
			farmData.put(nm, Double.parseDouble(bits[i]));
			i++;
		}
		for ( CropType ct:fbsCropsKeySet){
			i=crops.get(ct).setValues(i,bits);
		}
		farmId=(int)Math.round(farmData.get(FBSFarmDatum.FARMID));
		p2000reader=new FBSPost2000DataReader(farmId,crops,this);
	}
	public boolean addPost2000Datum(FBSPost2000DataPoint ident,double value) throws BadDataException {
		return p2000reader.addDatum(ident, value);
	}
		

	
	/** Checks the header of a CSV file to ensure that is was created with the writeAllToCSV function of this class */
	public static boolean checkCSVHeader(String header){
		String[] bits=header.split(",");
		int i=0;
		for ( FBSFarmDatum nm:FBSFarmDatum.values()){
			if (!bits[i].contentEquals(nm.RName)){
				return false;
			}
			i++;
		}
		for ( CropType ct: fbsCropsKeySet){
			String[] thisCropLabels=FBSCropData.allToCSVCropLabels(ct).split(",");
			for ( int j=0;j<thisCropLabels.length;j++){
				if ( !bits[i].contentEquals(thisCropLabels[j])){
					return false;
				}
				i++;
			}
		}
		return true;
	}
	
	public static String allToCSVHeader(){
		StringBuffer buff=new StringBuffer();
		for ( FBSFarmDatum nm:FBSFarmDatum.values()){
			buff.append(nm.RName+",");
		}
		for ( CropType ct:fbsCropsKeySet){
			buff.append(FBSCropData.allToCSVCropLabels(ct));
			buff.append(",");
		}
		buff.deleteCharAt(buff.length()-1); // Delete last comma
		buff.append("\n");
		return buff.toString();
	}
	
	public String dumpAll(){
		StringBuffer buff = new StringBuffer();
		DecimalFormat fmt = new DecimalFormat("#00.00");
		for ( FBSFarmDatum nm:FBSFarmDatum.values()){
			buff.append(nm+" ");
			if ( farmData.containsKey(nm)){
				buff.append(fmt.format(farmData.get(nm))+"\n");
			} else {
				buff.append(0.0+"\n");
			}
		}
		buff.append(FBSCropData.allToCSVLabels()+"\n");
		for ( FBSCropData cdata: crops.values()){
			buff.append(cdata.cropType+" : "+cdata.allToCSV(fmt));
			buff.append("\n");
		}
		return buff.toString();
	}
	
	public String writeAllToCSV(){
		StringBuffer buff = new StringBuffer();
		DecimalFormat fmt = new DecimalFormat("#00.00");
		for ( FBSFarmDatum nm:FBSFarmDatum.values()){
			if ( farmData.containsKey(nm)){
				buff.append(fmt.format(farmData.get(nm))+",");
			} else {
				buff.append(0.0+",");
			}
		}
		int i=0;
		for ( FBSCropData cdata: crops.values()){
			buff.append(cdata.allToCSV(fmt));
			i++;
			if ( i < crops.size()){
				buff.append(",");
			}
		}
		
		buff.append("\n");
		return buff.toString();
	}
	
	public void writeAllToCSV(FileWriter writer) throws IOException {
		writer.write(writeAllToCSV());
		writer.flush();
	}
	
	public String toString(){
		return writeAllToCSV();
	}

}
